﻿using System.Collections.Generic;
using Redbrick.Silverlight.Common;
using System.Collections.ObjectModel;

namespace Moria.Common.Model
{
	/// <summary>
	/// store_type
	/// </summary>
	public class Store : Observable
	{

		#region Consts & Settings

		public const int MaxStores = 6;
		public const int STORE_INVEN_MAX = 24;   /* Max number of discrete objs in inven  */
		public const int STORE_CHOICES = 26;   /* NUMBER of items to choose stock from  */
		public const int STORE_MIN_INVEN = 10;   /* Min diff objs in stock for auto sell  */
		public const int STORE_MAX_INVEN = 18;   /* Max diff objs in stock for auto buy   */
		public const int STORE_TURN_AROUND = 9;   /* Amount of buying and selling normally */
		public const int OBJ_TOWN_LEVEL = 7;    /* Town object generation level	       */

		#endregion Consts & Settings

		#region Declarations

		/* object_list[] index of objects that may appear in the store */
		private readonly int[,] store_choice = 
        {
        /* General Store */	{366,365,364, 84, 84,365,123,366,365,350,349,348,347,346,346,345,345,345,344,344,344,344,344,344,344,344},
        /* Armory	 */         { 94, 95, 96,109,103,104,105,106,110,111,112,114,116,124,125,126,127,129,103,104,124,125, 91, 92, 95, 96},
        /* Weaponsmith	 */   { 29, 30, 34, 37, 45, 49, 57, 58, 59, 65, 67, 68, 73, 74, 75, 77, 79, 80, 81, 83, 29, 30, 80, 83, 80, 83},
        /* Temple	 */         {322,323,324,325,180,180,233,237,240,241,361,362, 57, 58, 59,260,358,359,265,237,237,240,240,241,323,359},
        /* Alchemy shop	 */   {173,174,175,351,351,352,353,354,355,356,357,206,227,230,236,252,253,352,353,354,355,356,359,363,359,359},
        /* Magic-User store*/ {318,141,142,153,164,167,168,140,319,320,320,321,269,270,282,286,287,292,293,294,295,308,269,290,319,282}
        };

		InventoryRecord _currentItem = null;

		#endregion Declarations

		#region Constructor

		public Store(StoreType storeType)
		{
			this.store_inven = new ObservableCollection<InventoryRecord>();
			this.Type = storeType;

			int i = StoreOwner.owners.Count / MaxStores;
			this.Owner = StoreOwner.owners[MaxStores * (Dice.RandInt(i) - 1) + (int)storeType];
			this.InsultCur = 0;
			this.Open = 0;
			this.GoodBuy = 0;
			this.BadBuy = 0;

		}

		#endregion Constructor

		#region Methods

		/// <summary>
		/// Destroy an item in the stores inventory.  Note that if
		/// "one_of" is false, an entire slot is destroyed
		/// </summary>
		internal void store_destroy(int item_val, bool one_of)
		{
			InventoryItem i_ptr = this.store_inven[item_val].InventoryItem;

			// for single stackable objects, only destroy one half on average,
			//this will help ensure that general store and alchemist have
			//reasonable selection of objects 
			int number = 1;
			if ((i_ptr.SubCategory >= InventoryItem.ITEM_SINGLE_STACK_MIN) &&
				 (i_ptr.SubCategory <= InventoryItem.ITEM_SINGLE_STACK_MAX))
			{
				if (!one_of) number = Dice.RandInt((int)i_ptr.ItemCount);
			}
			else
			{
				number = i_ptr.ItemCount;
			}

			if (number != i_ptr.ItemCount)
			{
				i_ptr.ItemCount -= number;
			}
			else
			{
				this.store_inven.RemoveAt(item_val);
			}
		}

		/// <summary>
		/// 
		/// </summary>
		internal void store_create()
		{
			int tries = 0;
			do
			{
				int i = store_choice[this.StoreNum, Dice.RandInt(STORE_CHOICES) - 1];
				InventoryItem t_ptr = new InventoryItem(i);
				t_ptr.magic_treasure(OBJ_TOWN_LEVEL);
				if (this.store_check_num(t_ptr))
				{
					if ((t_ptr.Cost > 0) &&	/* Item must be good	*/
						 (t_ptr.Cost < this.Owner.max_cost))
					{
						/* equivalent to calling ident_spell(), except will not
					  change the object_ident array */
						t_ptr.store_bought();
						this.store_carry(t_ptr);
						tries = 10;
					}
				}
				tries++;
			}
			while (tries <= 3);
		}

		private int store_carry(InventoryItem t_ptr)
		{
			int ipos = -1;
			int dummy = 0;
			int icost = 0;
			if (this.sell_price(out icost, out dummy, t_ptr) > 0)
			{
				int item_val = 0;
				int item_num = t_ptr.ItemCount;
				bool flag = false;
				ItemType typ = t_ptr.Type;
				int subt = t_ptr.SubCategory;
				if (this.store_inven.Count > 0)
				{
					do
					{
						InventoryItem i_ptr = this.store_inven[item_val];
						if (typ == i_ptr.Type)
						{
							if (subt == i_ptr.SubCategory && /* Adds to other item	*/
							subt >= InventoryItem.ITEM_SINGLE_STACK_MIN
							&& (subt < InventoryItem.ITEM_GROUP_MIN || i_ptr.Misc == t_ptr.Misc))
							{
								ipos = item_val;
								i_ptr.ItemCount += item_num;
								/* must set new scost for group items, do this only for items
									strictly greater than group_min, not for torches, this
									must be recalculated for entire group */
								if (subt > InventoryItem.ITEM_GROUP_MIN)
								{
									sell_price(out icost, out dummy, i_ptr);
									this.store_inven[item_val].BaseCost = -icost;
								}
								/* must let group objects (except torches) stack over 24
									since there may be more than 24 in the group */
								else if (i_ptr.ItemCount > 24)
									i_ptr.ItemCount = 24;
								flag = true;
							}
						}
						else if (typ > i_ptr.Type)
						{		/* Insert into list		*/
							this.insert_store(item_val, icost, t_ptr);
							flag = true;
							ipos = item_val;
						}
						item_val++;
					}
					while ((item_val < this.Counter) && (!flag));
				}

				if (!flag)	/* Becomes last item in list	*/
				{
					this.insert_store(this.Counter, icost, t_ptr);
					ipos = this.Counter - 1;
				}
			}
			return ipos;
		}
		/// <summary>
		///  Asking price for an item				-
		/// </summary>
		int sell_price(out int max_sell, out int min_sell, InventoryItem item)
		{
			int i = item.Value;
			min_sell =
				 max_sell = -1;

			/* check item.cost in case it is cursed, check i in case it is damaged */
			if ((item.Cost > 0) && (i > 0))
			{
				// i = i * rgold_adj[this.Owner.Race][py.misc.prace] / 100;
				if (i < 1) i = 1;
				max_sell = i * this.Owner.max_inflate / 100;
				min_sell = i * this.Owner.min_inflate / 100;
				if (min_sell > max_sell) min_sell = max_sell;
				return (i);
			}
			/* don't let the item get into the store inventory */
			return (0);
		}

		/* Insert INVEN_MAX at given location	*/
		private void insert_store(int pos, int icost, InventoryItem i_ptr)
		{
			this.store_inven.Insert(pos, new InventoryRecord() { BaseCost = (-icost), InventoryItem = i_ptr });
		}

		private bool store_check_num(InventoryItem t_ptr)
		{
			if (this.Counter < STORE_INVEN_MAX)
			{
				return true;
			}
			else if (t_ptr.SubCategory >= InventoryItem.ITEM_SINGLE_STACK_MIN)
			{
				foreach (InventoryRecord i_ptr in this.store_inven)
				{
					/* note: items with subval of gte ITEM_SINGLE_STACK_MAX only stack if their subvals match */
					if (i_ptr.InventoryItem.Type == t_ptr.Type && i_ptr.InventoryItem.SubCategory == t_ptr.SubCategory
						 && (i_ptr.InventoryItem.ItemCount + t_ptr.ItemCount < 256)
						 && (t_ptr.SubCategory < InventoryItem.ITEM_GROUP_MIN
						 || (i_ptr.InventoryItem.Misc == t_ptr.Misc)))
					{
						return true;
					}
				}
			}
			return false;
		}

		#endregion Methods

		#region Events


		#endregion Events

		#region Properties
		
		public InventoryRecord CurrentItem
		{
			get { return this._currentItem; }
			set
			{
				if (value == this._currentItem) return;
				this._currentItem = value;
				this.RaisePropertyChanged(() => this.CurrentItem);
			}
		}


		public StoreType Type { get; private set; }
		public int StoreNum { get { return ((int)this.Type) - 1; } }
		public int Open { get; private set; }
		public int InsultCur { get; set; }
		public StoreOwner Owner { get; private set; }
		public int Counter { get { return this.store_inven.Count; } }
		public int GoodBuy { get; private set; }
		public int BadBuy { get; private set; }
		public ObservableCollection<InventoryRecord> store_inven { get; private set; }

		#endregion Properties

	}
}
